using System;
using System.Text;
using System.Data;
using System.Collections;
using System.Collections.Generic;

using Framework;
using SchemaMonitor.json;
using SchemaMonitor.json.dict;

namespace SchemaMonitor
{
    public interface ISchema
    {
        string UrlGetByType(CType apiUrl, CUser token);
        string UrlGetToken(CUser token);
        string UrlExtendToken(CUser token);
    }


    //Table-Mapping Class (Customisable half)
    public partial class CSchema : ISchema
    {
		#region WEB/APi
		public string UrlCreateAlbum(CUser u) { return UrlCreateAlbum(u.UserId, u.UserToken); }
		public string UrlCreateAlbum(long userId, string userToken)
		{
			string s = string.Concat(SchemaUrlBase, userId, "/albums");
			return Param(s, "access_token", userToken);
		}


		public string UrlGetMetadata(string fullId, CUser u) { return UrlGetMetadata(fullId, u.UserToken); }
        public string UrlGetMetadata(string fullId, string userToken)
        {
            string s = string.Concat(SchemaUrlBase, fullId);
            s = Param(s, "metadata", "1");
            s = Param(s, "access_token", userToken);
            return s;
        }


        public string UrlGetToken(CUser u) { return UrlGetToken(u.UserToken); }
        public string UrlGetToken(string userToken)
        {
            string s = string.Concat(SchemaUrlBase, SchemaUrlGetToken);
            s = s.Replace("{id}", SchemaApiClientId);
            s = s.Replace("{secret}", ApiSecret);
            s = s.Replace("{token}", userToken);
            return s;
        }


        public string UrlExtendToken(CUser u) { return UrlExtendToken(u.UserToken); }
        public string UrlExtendToken(string userToken)
        {
            return string.Concat(SchemaUrlBase, "debug_token",
                                "?input_token=", userToken,
                                "&access_token =", AccessToken);
        }

        public string UrlGetByType_Prev(CType t, CUser u, string before)
        {
            return Param(UrlGetByType(t, u), "before", before);
        }
        public string UrlGetByType_Next(CType t, CUser u, string after)
        {
            return Param(UrlGetByType(t, u), "after", after);
        }
        public string UrlGetByType(CType t, CUser u)
        {
            var url = UrlApi(t);
            url = Param(url, "access_token", u.UserToken);
            if (! t.HasMeta)
                url = Param(url, "metadata", "1");

			switch (t.TypeName.ToLower())
			{
				case "post":
					url = string.Concat(url, Facebook.FIELDS_POSTS);
					break;
                case "comment":
                    url = string.Concat(url, Facebook.FIELDS_COMMENTS);
                    break;
                case "like":
                    url = string.Concat(url, Facebook.FIELDS_LIKES);
                    break;
                case "attachment":
                    url = string.Concat(url, Facebook.FIELDS_ATTACHMENTS);
                    break;
                case "photo":
                    url = string.Concat(url, Facebook.FIELDS_PHOTOS);
                    break;
            }
            return url;
        }
        private string UrlApi(CType t)
        {
            return string.Concat(SchemaUrlBase, t.TypeApiPath);
        }

        //Utility
        public static string Param(string url, string name, string value)
        {
            if (url.Contains("?"))
                return string.Concat(url, "&", name, "=", value);
            else
                return string.Concat(url, "?", name, "=", value);
        }
        #endregion


        #region Constants
        public static class Facebook
        {
            internal const string NAME          = "Facebook";
            internal const string URL_BASE      = "https://graph.facebook.com/";
            internal const string URL_GET_TOKEN = "oauth/access_token?client_id={id}&client_secret={secret}&grant_type=fb_exchange_token&fb_exchange_token={token}";

            internal const string CLIENT_ID     = "1916464281903142";
            internal const string CLIENT_TOKEN  = "b9d56a68c8f229e161437380ff8b7dc9";
            internal const string CLIENT_SECRET = "8a31b481d411969eed5b7d87e6cbd1e5";

            public const string FIELDS_PHOTOS = "&fields=id,url,name,album,width,height,from,created_time,name_tags,page_story_id,picture,target,images,comment_count,like_count,likes,comments{id,from,message,created_time,parent,attachment,object,likes,comments}";
            public const string FIELDS_COMMENTS = "&fields=created_time,from,message,comment_count,like_count,attachment,id,likes,parent,object,comments{id,from,message,created_time,parent,attachment,object,likes,comments}";
            public const string FIELDS_LIKES = "&fields=id,name,pic";
            public const string FIELDS_ATTACHMENTS = "&fields=subattachments.limit(42),target,type,title,url,media";
            public const string FIELDS_POSTS = "&fields=attachments{subattachments.limit(42),target,type,title,url,media},comments{id,from,message,created_time,parent,attachment,object,likes,comments},likes,child_attachments,full_picture,from,id,message,name,object_id,privacy,story,shares,source,status_type,updated_time,place,application,properties&show_expired=true";
        }
        #endregion

        #region Constructors (Public)
        //Default Connection String
        public CSchema() : base() {}

        //Alternative Connection String
        public CSchema(CDataSrc dataSrc) : base(dataSrc) {}
        
        //Hidden  (UI code should use cache instead)
        protected internal CSchema(int schemaId) : base(schemaId) {}
        protected internal CSchema(CDataSrc dataSrc, int schemaId) : base(dataSrc, schemaId) {}
        protected internal CSchema(CDataSrc dataSrc, int schemaId, IDbTransaction txOrNull) : base(dataSrc, schemaId, txOrNull) { }
        #endregion

        #region Default Values
        protected override void InitValues_Custom()
        {
            _schemaCreated = DateTime.Now;
        }
        #endregion
        
        #region Default Connection String
        protected override CDataSrc DefaultDataSrc()  { return CDataSrc.Default;  }
        #endregion

        #region Properties - Relationships
        //Relationships - Foriegn Keys (e.g parent)

        //Relationships - Collections (e.g. children)
        public   CUserList Users()                 {   return new CUser(DataSrc).SelectBySchemaId(     this.SchemaId    ); } 
        public   CUserList Users(CPagingInfo pi)   {   return new CUser(DataSrc).SelectBySchemaId(pi,  this.SchemaId    ); } 
        internal CUserList Users(IDbTransaction tx){   return new CUser(DataSrc).SelectBySchemaId(     this.SchemaId, tx); } 
        public   int             UsersCount()            {   return new CUser(DataSrc).SelectCountBySchemaId(this.SchemaId    ); }

        public    CTypeList Types                     {  get { return CType.Cache.GetBySchemaId(          this.SchemaId); }  }
        protected CTypeList Types_(IDbTransaction tx) {        return new CType(DataSrc).SelectBySchemaId(this.SchemaId, tx); } //Only used for cascade deletes
        #endregion

        #region Properties - Customisation

        private static string _accessToken;
        public static string AccessToken
        {
            get
            {
                if (null == _accessToken)
                {
                    var u = new CUser();
                    var uu = u.SelectByName("jeremy.connell7");
                    if (uu.Count == 1)
                        _accessToken = uu[0].UserName;
                }
                return _accessToken;
            }
        }
        #endregion

        #region Encryption
        public string ClientToken //Decrypt (and Reencrypt any plain text
        {
            get
            {
                //One-off
                var temp = SchemaApiTokenTemp;
                if (!string.IsNullOrEmpty(temp))
                    this.ClientToken = temp;

                //Normal
                var encr = SchemaApiTokenEncr;
                if (null != encr)
                    return CBinary.DecryptRijndaelAsStr(encr);

                //None
                return string.Empty;
            }
            set
            {
                if (null == value)
                {
                    SchemaApiTokenEncr = null;
                    SchemaApiTokenTemp = null;
                }
                else
                {
                    SchemaApiTokenEncr = CBinary.EncryptRijndael(value);
                    SchemaApiTokenTemp = null;
                }
                Save();
            }
        }
        public string ApiSecret   //Decrypt (and Reencrypt any plain text
        {
            get
            {
                //One-off
                var temp = SchemaApiSecretTemp;
                if (!string.IsNullOrEmpty(temp))
                    this.ApiSecret = temp;

                //Normal
                var encr = SchemaApiSecretEncr;
                if (null != encr)
                    return CBinary.DecryptRijndaelAsStr(encr);

                //None
                return string.Empty;
            }
            set
            {
                if (null == value)
                {
                    SchemaApiSecretEncr = null;
                    SchemaApiSecretTemp = null;
                }
                else
                {
                    SchemaApiSecretEncr = CBinary.EncryptRijndael(value);
                    SchemaApiSecretTemp = null;
                }
                Save();
            }
        }
        #endregion

        #region Save/Delete Overrides
        //Can Override base.Save/Delete (e.g. Cascade deletes, or insert related records)
        public override void Save(IDbTransaction txOrNull)
        {
            SchemaUpdated = DateTime.Now;
            base.Save(txOrNull);
        }

        #endregion

        #region Custom Database Queries
        //(Not normally required for cached classes, use list class for searching etc)
        //For Stored Procs can use: MakeList (matching schema), or DataSrc.ExecuteDataset (reports etc)
        //For Dynamic sql, can use: SelectSum, SelectDistinct, SelectCount, SelectWhere (inherited methods)
        #endregion

        #region Searching (Optional)
        //For cached classes, custom seach logic resides in static methods on the list class
        // e.g. CSchema.Cache.Search("...")

        //See also the auto-generated methods based on indexes
        //' e.g. CSchema.Cache.GetBy...
        #endregion

        #region Caching Details
        //Cache Key
        internal static string CACHE_KEY = typeof(CSchema).ToString();    //TABLE_NAME

        //Cache data
        private static CSchemaList LoadCache()
        {
            return new CSchema().SelectAll();
        }
        //Cache Timeout
        private static void SetCache(CSchemaList value)
        {
            if (null != value)  
                value.Sort(); 
            CCache.Set(CACHE_KEY, value);    //Optional parameter can override timeout (otherwise uses config-settings, which default to 3hrs)
        }
        //Helper Method
        private CSchema CacheGetById(CSchemaList list)  { return list.GetById(this.SchemaId);    }
        #endregion

        #region Cloning
        public CSchema Clone(CDataSrc target, IDbTransaction txOrNull) //, int parentId)
        {
            //Shallow copy: Copies the immediate record, excluding autogenerated Pks
            CSchema copy = new CSchema(this, target);

            //Deep Copy - Child Entities: Cloned children must reference their cloned parent
            //copy.SampleParentId = parentId;

            copy.Save(txOrNull);

            //Deep Copy - Parent Entities: Cloned parents also clone their child collections
            //this.Children.Clone(target, txOrNull, copy.SchemaId);

            return copy;
        }
        #endregion

        #region ToXml
        protected override void ToXml_Custom(System.Xml.XmlWriter w)
        {
            //Store(w, "Example", this.Example)
        }
        #endregion



        #region Static - Config Lockdown
        //Config-driven filter
        public static bool HasFilter    {  get { return OnlySchemaId > 0; } }
        public static int OnlySchemaId  {  get { return Only.SchemaId; } }
        public static int DefaultId     {  get { return OnlySchemaId > 0 ? OnlySchemaId : FB.SchemaId ; } }
        private static CSchema _only;
        public static CSchema Only
        {
            get
            {
                //Cached
                if (null != _only)
                    return _only;

                //* in Config => all
                var name = CConfig.OnlyUseSchema;
                if ("*" == name)
                {
                    _only = new CSchema();
                    return _only;
                }

                //Is Facebook
                if (name == FB.SchemaName)
                {
                    _only = FB;
                    return _only;
                }

                //Something new
                _only = GetOrCreate(name);
                return _only;
            }
        }

        //Sql helpers
        internal static CCriteriaList InScope(string colName)
        {
            return InScope(new CCriteriaList(1), colName);
        }
        internal static CCriteriaList InScope(CCriteriaList where, string colName)
        {
            if (HasFilter)
                where.Add(colName, OnlySchemaId);
            return where;
        }
        #endregion

        #region Static - Hardcoded
        public readonly static CSchema FB = CreateFacebook;
        private static CSchema CreateFacebook
        {
            get
            {
                return GetOrCreate(
                    Facebook.NAME, 
                    Facebook.URL_BASE, 
                    Facebook.URL_GET_TOKEN,
                    Facebook.CLIENT_ID,
                    Facebook.CLIENT_TOKEN,
                    Facebook.CLIENT_SECRET);
            }
        }
        #endregion

        #region Static - GetOrCreate
        public static CSchema GetOrCreate(string name, string urlBase = null, string urlGetToken=null, string clientId=null, string clientToken=null, string secret = null)
        {
            var ss = CSchema.Cache.GetByName(name);
            if (ss.Count == 1)
                return ss[0];

            var s = new CSchema();
            s.SchemaName = name;
            s.SchemaUrlBase = urlBase;
            s.SchemaUrlGetToken = urlGetToken;
            s.SchemaApiClientId = clientId;
            s.ApiSecret = secret;
            s.ClientToken = clientToken;
            s.Save();
            return s;
        }
        #endregion

    }
}